#include "CentralCache.h"
#include <unistd.h>

CentralCache* CentralCache::m_centralCache = new CentralCache;

//从SpanLists或者Page cache 获取一个非空的span
Span* CentralCache::GetOneSpan(SpanList& list, size_t size)
{
	//从SpanLists 获取一个非空的span
	Span* it = list.Begin();  //获取链表第一个有效节点
	while (it != list.End())
	{
		if (it->freeList != nullptr)
		{
			return it;
		}
		else
		{
			it = it->next;
		}
	}

	//先把central cache的桶锁解掉，这样如果其他线程释放内存对象回来不会阻塞
	list.m_mtx.unlock();

	Span* span = nullptr;
	{
		std::unique_lock<std::mutex> lock(PageCache::GetInstance()->m_pageMtx);
		
		//向 page cache 分配一块内存
		span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));
		span->isUsed = true;
		span->objSize = size;
	}

	//对获取的span进行切分，不需要加锁，因为其他线程访问不到这个span

	char* start = (char*)(span->pageId << PAGE_SHIFT); //还原指针
	size_t bytes = span->n << PAGE_SHIFT;	//计算内存大小
	char* end = start + bytes;

	//把大块内存切成自由链表连接起来
	//先切一块下来去做头，方便尾插
	span->freeList = start;

	void* tail = span->freeList;
	start += size;
	while (start < end)
	{
		NextObj(tail) = start;
		tail = NextObj(tail);//tail = start;
		start += size;
		// usleep(100); 	//当虚拟机性能弱，并且切分的内存比较小时这里容易崩溃，适当可适当减慢切分速度
	}

	*(void**)tail = nullptr;


	list.m_mtx.lock();
	list.PushFront(span);

	return span;
}

// 从中心缓存获取一定数量的对象给thread cache
size_t CentralCache::FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size)
{
	size_t index = SizeClass::Index(size);	//计算要映射到那个桶

	m_spanLists[index].m_mtx.lock();
	Span* span = GetOneSpan(m_spanLists[index], size);

	assert(span);
	assert(span->freeList);

	//从span中获取batchNum个对象
	//如果不够batchNum,有多少拿多少
	// start = span->freeList;
	// end = start;
	
	start = span->freeList;
	end = start;
	size_t i = 0;
	size_t actualNum = 1;
	while (i < batchNum - 1 && *(void**)end != nullptr)//end 往后走batchNum -1个
	{
		end = *(void**)end;
		++i;
		++actualNum;
	}

	span->freeList = *(void**)end;  //修改头指针
	*(void**)end = nullptr;
	span->useCount += actualNum;  //被使用的个数

	m_spanLists[index].m_mtx.unlock();
	return actualNum;
}

// 将一定数量的对象释放到span
void CentralCache::ReleaseListToSpans(void* start, size_t size)
{
	size_t index = SizeClass::Index(size);	//计算要映射到那个桶

	m_spanLists[index].m_mtx.lock();

	while (start)
	{
		Span* span = PageCache::GetInstance()->MapObjectToSpan(start); //找出对应的span

		//头插
		*(void**)start = span->freeList;
		span->freeList = start;

		--span->useCount;

		if (span->useCount == 0) //如果span切分出去的的所有小内存都回来了
		{
			//该span可以归还给page cache,page cache再可以去做前后页的合并

			m_spanLists[index].Erase(span);
			span->freeList = span->next = span->prev = nullptr;

			m_spanLists[index].m_mtx.unlock();

			//span还给page cache
			PageCache::GetInstance()->m_pageMtx.lock();
			PageCache::GetInstance()->ReleaseSpanToPageCache(span);
			PageCache::GetInstance()->m_pageMtx.unlock();

			m_spanLists[index].m_mtx.lock();
		}

		start = *(void**)start;
	}

	m_spanLists[index].m_mtx.unlock();
}